package com.qf.business.user.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qf.business.user.mapper.RedEnvelopesMapper;
import com.qf.business.user.service.RedEnvelopesDetailsService;
import com.qf.business.user.service.RedEnvelopesService;
import com.qf.business.user.service.ScoreDetailsService;
import com.qf.business.user.service.UserService;
import com.qf.commons.redis.cache.annotation.CacheDel;
import com.qf.commons.redis.lock.aop.CsLock;
import com.qf.commons.web.aop.UserInfo;
import com.qf.data.user.entity.RedEnvelopes;
import com.qf.data.user.entity.RedEnvelopesDetails;
import com.qf.data.user.eunm.ScoreActionType;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;

/**
 * (RedEnvelopes)表服务实现类
 *
 * @author makejava
 * @since 2022-07-20 10:21:24
 */
@Service
@Slf4j
public class RedEnvelopesServiceImpl extends ServiceImpl<RedEnvelopesMapper, RedEnvelopes> implements RedEnvelopesService {

    @Autowired
    private UserService userService;

    @Autowired
    private RedEnvelopesMapper redEnvelopesMapper;

    @Autowired
    private RedEnvelopesDetailsService redEnvelopesDetailsService;

    @Autowired
    private ScoreDetailsService scoreDetailsService;

    /**
     * 发送红包
     *
     * 开启事务 -> 添加锁 -> 操作 -> 解锁 -> 提交事务    事务和事务 隔离性  事务A 无法看到 事务B未提交的数据
     * @param redEnvelopes
     * @return
     */
    @Override
    @Transactional
    @CsLock("'lock_user_score_' + T(com.qf.commons.web.aop.UserInfo).getUid()")
    @CacheDel("'cache_user_' + T(com.qf.commons.web.aop.UserInfo).getUid()")
    public long sendRedEnvelopes(RedEnvelopes redEnvelopes) {

        //1、填充红包对象
        //谁发的红包
        Long uid = UserInfo.getUid();
        redEnvelopes.setUid(uid);
        //过期时间
        Date outTime = new Date(System.currentTimeMillis() + 24 * 60 * 60 * 1000);
        redEnvelopes.setTimeout(outTime);

        //2、查询用户的积分
        Integer score = userService.queryScore(uid);
        log.info("[send red] - 查询用户当前剩余积分 - {}", score);

        //3、判断积分是否足够
        if (score < redEnvelopes.getScore()) {
            //说明积分不足
            return -1;
        }

        //4、积分足够 - 扣减积分
        long scoreDetailsId = userService.updateScoreByUid(uid, redEnvelopes.getScore(), 1, ScoreActionType.RED_SEND.getType());
        log.info("[send red] - 扣减用户积分，并且生成积分流水 - {}", scoreDetailsId);

        //5、生成红包
        super.save(redEnvelopes);
        //获取红包id
        long redId = redEnvelopes.getId();
        log.info("[send red] - 保存红包信息，生成红包id - {}", redId);

        //6、修改流水的业务id
        scoreDetailsService.updateBusId(scoreDetailsId, redId);
        log.info("[send red] - 同步修改积分流水的业务id号 - {}", scoreDetailsId);

        return redId;
    }

    /**
     * 抢红包的业务流程
     * @param redid
     * @return
     */
    @Override
    @Transactional
    @CsLock("'lock_red_envlopoes_' + #redid")
    @CacheDel("'cache_user_' + T(com.qf.commons.web.aop.UserInfo).getUid()")
    public long robRedEnvelopoes(long redid) {

        //1、根据红包id，查询红包状态 - 红包不存在、红包已经被抢完、红包正在抢的过程中、红包已经过期....
        //状态值：0 - 红包正在抢的过程中 1 - 红包已经被抢完 2 - 红包已经过期
        RedEnvelopes redEnvelopes = this.queryRedEnvelopesDetails(redid);
        if (redEnvelopes == null) {
            return -1;
        }

        if (redEnvelopes.getStatus() == 1) {
            return -2;
        }

        if (redEnvelopes.getStatus() == 2) {
            return -3;
        }

        //判断红包明细是否有当前用户的抢红包的记录
        Long count = redEnvelopesDetailsService.query().eq("rid", redid).eq("uid", UserInfo.getUid()).count();
        if (count > 0) {
            return -4;
        }

        //

        //2、计算当前用户可以抢的积分值
        int robScore = 0;//抢到的积分值

        //如果只剩最后一个名额，那剩下的所有积分全部被当前用户抢到
        if(redEnvelopes.getHasCount() == 1){
            robScore = redEnvelopes.getHasScore();
        } else {
            //判断类型
            if (redEnvelopes.getType() == 0) {
                //固定红包
                robScore = redEnvelopes.getHasScore() / redEnvelopes.getHasCount();
            } else {
                //随机红包 - 二倍均值法
                // 10000 10
                // 1 - 1 ~ 10000 / 10 * 2 （1 ~ 2000） - 1000
                // 2 - 1 ~ 9000 / 9 * 2 (1 ~ 2000) - 1000

                //[0 ~ 1)
                robScore = (int)(Math.random() * (redEnvelopes.getHasScore() / redEnvelopes.getHasCount() * 2 - 1) + 1);
            }
        }

        //3、生成一条红包明细
        RedEnvelopesDetails redEnvelopesDetails = new RedEnvelopesDetails();
        redEnvelopesDetails.setUid(UserInfo.getUid());
        redEnvelopesDetails.setRid(redid);
        redEnvelopesDetails.setType(0);
        redEnvelopesDetails.setScore(robScore);
        redEnvelopesDetails.setStatus(1);//1 - 已抢红包 2 - 退回红包
        redEnvelopesDetailsService.save(redEnvelopesDetails);

        //4、添加用户积分
//        long detailsId = userService.updateScoreByUid(UserInfo.getUid(), robScore, 0, ScoreActionType.RED_ROB.getType());
//        //同步设置流水中的业务id号 - 红包id
//        scoreDetailsService.updateBusId(detailsId, redid);

        //5、如果红包被抢完，需要修改红包的状态
        if(redEnvelopes.getHasCount() == 1) {
            //红包被抢完，修改红包的状态
            redEnvelopesMapper.updateStatus(redid, 1);//表示当前红包已经被抢完
            //计算运气王
            redEnvelopesDetailsService.updateDetailsType(redid);
        }

        return robScore;
    }

    @Override
    public void robRedEnvelopoesPlainB(long redid, long uid, long score) {

    }

    /**
     * 根据红包id 查询红包信息 以及 红包剩余的份额和积分信息
     * @param redid
     * @return
     */
    @Override
    public RedEnvelopes queryRedEnvelopesDetails(long redid) {
        return redEnvelopesMapper.queryRedEnvelopesDetails(redid);
    }

    @Override
    public void redBack() {

    }

    /**
     *
     * 发红包 - 10个红包随机数 1 ~ 99991   1 ~ 4992 ....
     * 抢红包 -
     *   10个坑 -> redis
     *   1 -> 线程1
     *   2 -> 线程2
     * @param args
     */
    public static void main(String[] args) {
        int score = 100000;
        int count = 40;

        int sum = 0;

        int len = count;
        for (int i = 0; i < len; i++) {

            int robScore = 0;
            if(i == len - 1){
                robScore = score;
            } else {
                robScore = (int) (Math.random() * (score / count * 2 - 1) + 1);
//                robScore = (int) (Math.random() * (score - count + 1) + 1);
            }

            sum += robScore;

            System.out.println("第" + (i+1) + "个玩家抢到的红包数：" + robScore);

            score -= robScore;
            count--;
        }

        System.out.println("累加的结果：" + sum);
    }
}


